# 创建时间:2023/3/16 23:54
# 创建人:天霄
# 计划 :SenjoTwintail
# 文件 :LoadImage.py
# 基于 Python 3.8.2
# ========================================
# 文件简介
# ========================================
import pathlib
import plistlib
import pygame
from Api import uiApi
from BasePygame.Public import flagBox
from .CommonElement import SpriteCommon


class Loading:
    """加载中的图像"""
    def __init__(self, screen):
        """加载中..."""
        self._screen = screen
        screen_size = pygame.display.get_window_size()
        screen_sizeRect = pygame.Rect(0, 0, *screen_size)
        self.all_loadingLoads = imageLoad_from_plist(*uiApi.getUiPathPlist('loading'))
        self._screen_center = screen_sizeRect.center
        self._loadingImage = self._generate_loading()

    def _generate_loading(self):
        """生成拼接好后的loading图片"""
        all_loadingLoads = self.all_loadingLoads

        dot = all_loadingLoads['dot']  # 点
        bg = all_loadingLoads['bg']  # 弹窗背景
        briefs = all_loadingLoads['briefs']  # 胖次
        loading = all_loadingLoads['loading']  # “loading”
        tryagain = all_loadingLoads['tryagain']  # 重试按钮

        img = pygame.Surface((96, 103), pygame.SRCALPHA).convert_alpha()
        img.set_alpha(200)
        img.blit(briefs.surface, (0, 0))
        img.blit(loading.surface, (0, 74))
        img.blit(dot.surface, (79, 93))
        img.blit(dot.surface, (85, 93))
        img.blit(dot.surface, (91, 93))
        return img

    def loading(self):
        """显示加载中，立即刷新显示"""
        wRect = self._loadingImage.get_rect()
        wRect.center = self._screen_center
        self._screen.blit(self._loadingImage, wRect)  # 绘制
        pygame.display.update()
        flagBox.clickable = False


def change_image(imgpath, cutlocation=None, angle=0, scale=None):
    """处理图片（切割旋转缩放）

    :param imgpath: 图片路径
    :param cutlocation: 切割原图片的坐标(x,y,w,h)
    :param angle: 逆时针旋转角度
    :param scale: 图片缩放倍数
    """
    image = pygame.image.load(imgpath).convert_alpha()
    if cutlocation is not None:
        image = image.subsurface(cutlocation)
    if scale is not None:
        image = pygame.transform.rotozoom(image, angle, scale)
        # print(image.get_size())
    return image


class LoadImage:
    """图片对象处理"""
    function = None
    objectName = None

    def __init__(self, surface: pygame.Surface, offset=(0, 0), size=(0, 0)):
        """将图片对象赋予新方法（并不是精灵！）

        :param surface: 已切割好的图片对象
        :param offset: 原点偏移量
        :param size: 自定义点击区域大小，不设置则默认为图像大小
        """
        self.surface = surface
        self.offset = offset
        self.rect = self.surface.get_rect()
        self.rect.size = (self.rect.size[0]-self.offset[0], self.rect.size[1]-self.offset[1])
        self.clickRect = self.rect.copy()
        if size != (0, 0):
            self.clickRect = pygame.Rect((0, 0), size)
            self.clickRect.center = self.rect.center

    def __del__(self):
        """销毁对象"""
        del self.surface, self.rect, self.clickRect
        del self

    def setTopleft(self, location=(0, 0)):
        """设定图像的原点"""
        self.rect.topleft = location
        self.clickRect.center = self.rect.center
        return self

    def setBottomright(self, location=(0, 0)):
        """设定图像的右下角点"""
        self.rect.bottomright = location
        self.clickRect.center = self.rect.center
        return self

    def setBottomleft(self, location=(0, 0)):
        """设定图像的左下角点"""
        self.rect.bottomleft = location
        self.clickRect.center = self.rect.center
        return self

    def draw_in(self, surface, rect, params=None):
        """在图像上继续画一个图像"""
        if isinstance(surface, pygame.Surface):
            self.surface.blit(surface, rect)
            return self
        elif isinstance(surface, LoadImage):
            self.surface.blit(surface.surface, rect)
            return self
        raise KeyError(f'surface参数类型有误！只能为Surface或LoadImage！然而传入的类型是【{type(surface)}】')

    def draw_in_RectCenter(self, artSurface, frameRect):
        """在图像上的指定区域的中心继续画一个图像"""
        if not isinstance(frameRect, pygame.Rect):
            frameRect = pygame.Rect(*frameRect)
        if isinstance(artSurface, pygame.Surface):
            rect = artSurface.get_rect()
            rect.center = frameRect.center
            self.surface.blit(artSurface, rect)
            return self
        elif isinstance(artSurface, LoadImage):
            rect = artSurface.clickRect
            rect.center = frameRect.center
            self.surface.blit(artSurface.surface, rect)
            return self
        raise KeyError(f'artSurface参数类型有误！只能为Surface或LoadImage！然而传入的类型是【{type(artSurface)}】')

    def draw_RectCustom(self, artSurface, frameRect, same: str, offset=(0, 0)):
        """在图像上的指定区域的任意位置继续画一个图像

        :param artSurface: 主画面，Surface或LoadImage
        :param frameRect: 指定区域，Rect或4个数字
        :param same: 以本图的哪个rect为基准
        :param offset: 与该基准的偏移量
        :return:

        · same:
            - topleft：左上角坐标
            - topright：右上角坐标
            - bottomleft：左下角坐标
            - bottomright：右下角坐标
            - center：矩形中心坐标
            - midbottom：下侧中心点坐标
            - midleft：左侧中心点坐标
            - midright：右侧中心点坐标
            - midtop：上侧中心点坐标
        """
        if not isinstance(frameRect, pygame.Rect):
            frameRect = pygame.Rect(*frameRect)
        if isinstance(artSurface, pygame.Surface):
            rect = artSurface.get_rect()
            mysame = getattr(frameRect, same)
            setattr(rect, same, (mysame[0]+offset[0], mysame[1]+offset[1]))
            self.surface.blit(artSurface, rect)
            return self
        elif isinstance(artSurface, LoadImage):
            rect = artSurface.clickRect
            mysame = getattr(frameRect, same)
            setattr(rect, same, (mysame[0]+offset[0], mysame[1]+offset[1]))
            self.surface.blit(artSurface.surface, rect)
            return self
        raise KeyError(f'artSurface参数类型有误！只能为Surface或LoadImage！然而传入的类型是【{type(artSurface)}】')

    def draw_custom(self, artSurface, same: str, offset=(0, 0)):
        """在图像继续画一个图像

        :param artSurface: 主画面，Surface或LoadImage
        :param same: 以本图的哪个rect为基准
        :param offset: 与该基准的偏移量

        · same:
            - topleft：左上角坐标
            - topright：右上角坐标
            - bottomleft：左下角坐标
            - bottomright：右下角坐标
            - center：矩形中心坐标
            - midbottom：下侧中心点坐标
            - midleft：左侧中心点坐标
            - midright：右侧中心点坐标
            - midtop：上侧中心点坐标
        :return:
        """
        if isinstance(artSurface, pygame.Surface):
            rect = artSurface.get_rect()
            mysame = getattr(self.clickRect, same)
            setattr(rect, same, (mysame[0]+offset[0], mysame[1]+offset[1]))
            self.surface.blit(artSurface, rect)
            return self
        elif isinstance(artSurface, LoadImage):
            rect = artSurface.clickRect
            mysame = getattr(self.clickRect, same)
            setattr(rect, same, (mysame[0]+offset[0], mysame[1]+offset[1]))
            self.surface.blit(artSurface.surface, rect)
            return self
        raise KeyError(f'surface参数类型有误！只能为Surface或LoadImage！然而传入的类型是【{type(artSurface)}】')

    def draw_in_center(self, artSurface):
        """以本图为低，在本图像中心位置继续画一个图像

        :param artSurface: 主画面，Surface或LoadImage
        :return: 本体
        """
        if isinstance(artSurface, pygame.Surface):
            rect = artSurface.get_rect()
            rect.center = self.clickRect.center
            self.surface.blit(artSurface, rect)
            return self
        elif isinstance(artSurface, LoadImage):
            rect = artSurface.clickRect
            rect.center = self.clickRect.center
            self.surface.blit(artSurface.surface, rect)
            return self
        raise KeyError(f'surface参数类型有误！只能为Surface或LoadImage！然而传入的类型是【{type(artSurface)}】')

    def drawed_in_center(self, paperSurface):
        """以本图像为主画面，在图像中心位置添加背景

        :param paperSurface: 背景图（纸张），Surface或LoadImage
        :return: 绘制完成后的图LoadImage
        """
        if isinstance(paperSurface, pygame.Surface):
            rect = self.rect
            rect.center = paperSurface.get_rect().center
            paperSurface.blit(self.surface, rect)
            return LoadImage(paperSurface, self.offset, self.clickRect.size)
        elif isinstance(paperSurface, LoadImage):
            rect = self.rect
            rect.center = paperSurface.clickRect.center
            paperSurface.surface.blit(self.surface, rect)
            return LoadImage(paperSurface.surface, self.offset, self.clickRect.size)
        raise KeyError(f'surface参数类型有误！只能为Surface或LoadImage！然而传入的类型是【{type(paperSurface)}】')

    def draw_same_size(self, artSurface):
        """以本图为准，画一个相同尺寸的图覆盖在上面"""
        if isinstance(artSurface, pygame.Surface):
            artSurface = pygame.transform.scale(artSurface, self.clickRect.size)
            self.surface.blit(artSurface, (0, 0))
            return self
        elif isinstance(artSurface, LoadImage):
            artSurface = pygame.transform.scale(artSurface.surface, self.clickRect.size)
            self.surface.blit(artSurface, (0, 0))
            return self
        raise KeyError(f'surface参数类型有误！只能为Surface或LoadImage！然而传入的类型是【{type(artSurface)}】')

    def draw_sameAs_size(self, artSurface):
        """以传入为准，画一个相同尺寸的图覆盖在上面"""
        if isinstance(artSurface, pygame.Surface):
            self.surface = pygame.transform.scale(self.surface, artSurface.get_size())
            self.surface.blit(artSurface, (0, 0))
            return self.surface
        elif isinstance(artSurface, LoadImage):
            self.surface = pygame.transform.scale(self.surface, artSurface.clickRect.size)
            self.surface.blit(artSurface.surface, (0, 0))
            return self.surface
        raise KeyError(f'surface参数类型有误！只能为Surface或LoadImage！然而传入的类型是【{type(artSurface)}】')

    def drag(self, times: float, direction='w'):
        """横向或竖向拉伸图像

        :param times: 倍数
        :param direction: 方向（h竖向，w横向，wh/hw两个方向都缩放。默认横向）
        :return:
        """
        size = self.surface.get_size()
        if direction == 'w':
            size = (size[0]*times, size[1])
            self.rect.w *= times
            self.clickRect.w *= times
        elif direction == 'h':
            size = (size[0], size[1]*times)
            self.rect.h *= times
            self.clickRect.h *= times
        elif direction in ('wh', 'hw'):
            size = (size[0]*times, size[1]*times)
            self.rect.size = (self.rect.size[0]*times, self.rect.size[1]*times)
            self.clickRect.size = (self.clickRect.size[0]*times, self.clickRect.size[1]*times)
        self.surface = pygame.transform.scale(self.surface, size)
        return self

    def rechange(self, angle, scale):
        """旋转、缩放"""
        return pygame.transform.rotozoom(self.surface, angle, scale)

    def resize(self, size: tuple):
        """更改尺寸，返回新对象"""
        return LoadImage(pygame.transform.scale(self.surface, size))

    def as_size(self, surface):
        """变为与之尺寸相同的图像，返回Surface对象"""
        if isinstance(surface, pygame.Surface):
            self.surface = pygame.transform.scale(self.surface, surface.get_size())
            return self.surface
        elif isinstance(surface, LoadImage):
            self.surface = pygame.transform.scale(self.surface, surface.clickRect.size)
            return self.surface
        raise KeyError(f'surface参数类型有误！只能为Surface或LoadImage！然而传入的类型是【{type(surface)}】')

    def inClickRect(self):
        """判断当前坐标是否在可点击区域"""
        return self.clickRect.collidepoint(pygame.mouse.get_pos())

    def connect(self, function):
        """连接执行函数"""
        self.function = function

    def check(self, clickSound=None, flag=None, flagIsAdd=False):
        """如果点击且在点击区域内，则执行函数并返回自己、函数结果

        :param clickSound: 按键声音
        :param flag: 自定义判断标记，默认为点击抬起且未移动
        :param flagIsAdd: 该flag作为单独判断还是附件判断条件，默认否-单独判断
        :return: 执行则返回：自身、函数结果
        """
        if flag is None:
            flag = flagBox.clicked and not (flagBox.moving and flagBox.pressed[0])
        elif flagIsAdd:
            flag = flag and flagBox.clicked and not (flagBox.moving and flagBox.pressed[0])
        if self.inClickRect():
            if any(flagBox.clickOnce):
                flagBox.where_in = None
            if flag:
                flagBox.where_in = clickSound
                result = self.function()
                flagBox.fondled_where = None
                flagBox.fondled = False
                print(f'自身区域：{self.rect.topleft}{self.rect.bottomright}，可点击区域：{self.clickRect.topleft}{self.clickRect.bottomright}')
                return self, result
            return None, None
        return None, None

    def copy(self):
        """复制一个本对象，不复制函数"""
        copy_surface = self.surface.copy()
        copy_loadimage = LoadImage(copy_surface)
        copy_loadimage.rect = self.rect.copy()
        copy_loadimage.clickRect = self.clickRect.copy()
        copy_loadimage.offset = self.offset
        return copy_loadimage


def loadHalfImage(file, imgtype, initImage):
    """通过判断imgtype组合切片表情"""
    if imgtype != 'cut':
        return LoadImage(change_image(file))
    initHalf = LoadImage(change_image(initImage)).draw_in(change_image(file), (0, 0))
    return initHalf


def imageRects_from_plist(png_filepath: pathlib.Path, plist_filepath: pathlib.Path):
    """根据plist返回所有图像切割数据

    :param png_filepath: png文件绝对路径
    :param plist_filepath: plist文件绝对路径
    :return: 切割坐标、旋转角度、大小
    """
    imageRects = {}
    if not all((plist_filepath.exists(), png_filepath.exists())):
        raise FileNotFoundError("文件不存在！")
    with open(plist_filepath, 'rb') as f:
        plist_dict = plistlib.load(f)
    # print(plist_dict)
    to_list = lambda x: list(map(int, x.replace('{', '').replace('}', '').split(',')))
    for img_name, img_setting in plist_dict['frames'].items():
        rectlist = to_list(img_setting['frame'])
        sizelist = to_list(img_setting['sourceSize'])
        angle = 0
        if img_setting['rotated']:
            angle = 90
            rectlist[2], rectlist[3] = rectlist[3], rectlist[2]
        img_name = img_name.split('/')[-1].split('.')[0]
        imageRects[img_name] = (tuple(rectlist), angle, tuple(sizelist))
    return imageRects


def imageLoad_from_plist(png_filepath: pathlib.Path, plist_filepath: pathlib.Path):
    """根据plist返回所有LoadImage图片对象

    :param png_filepath: png文件绝对路径
    :param plist_filepath: plist文件绝对路径
    :return: 切割坐标、旋转角度、大小
    """
    imageSurfaces = {}
    if not all((plist_filepath.exists(), png_filepath.exists())):
        raise FileNotFoundError("文件不存在！")
    source_img = pygame.image.load(png_filepath)
    with open(plist_filepath, 'rb') as f:
        plist_dict = plistlib.load(f)
    # print(plist_dict)
    to_list = lambda x: list(map(int, x.replace('{', '').replace('}', '').split(',')))
    for img_name, img_setting in plist_dict['frames'].items():
        rectlist = to_list(img_setting['frame'])
        sizelist = to_list(img_setting['sourceSize'])
        offset = to_list(img_setting['offset'])
        angle = 0
        if img_setting['rotated']:
            angle = 90
            rectlist[2], rectlist[3] = rectlist[3], rectlist[2]
        image = source_img.copy().subsurface(tuple(rectlist))
        image = pygame.transform.rotozoom(image, angle, 1)
        imageLoaded = LoadImage(image, tuple(offset), tuple(sizelist))
        img_name = img_name.split('/')[-1].split('.')[0]
        imageSurfaces[img_name] = imageLoaded
    return imageSurfaces


def imageSprite_from_plist(png_filepath: pathlib.Path, plist_filepath: pathlib.Path):
    """根据plist配置生成所有图片精灵对象

    :param png_filepath: png文件绝对路径
    :param plist_filepath: plist文件绝对路径
    :return:
    """
    imageSprites = {}
    if not all((plist_filepath.exists(), png_filepath.exists())):
        raise FileNotFoundError("文件不存在！")
    source_img = pygame.image.load(png_filepath)
    with open(plist_filepath, 'rb') as f:
        plist_dict = plistlib.load(f)
    # print(plist_dict)
    to_list = lambda x: list(map(int, x.replace('{', '').replace('}', '').split(',')))
    for img_name, img_setting in plist_dict['frames'].items():
        rectlist = to_list(img_setting['frame'])
        sizelist = to_list(img_setting['sourceSize'])
        angle = 0
        # image = pygame.transform.scale(image, tuple(sizelist))  # 自定义缩放
        if img_setting['rotated']:
            angle = 90
            rectlist[2], rectlist[3] = rectlist[3], rectlist[2]
        image = source_img.subsurface(tuple(rectlist))
        image_Sprite = SpriteCommon(image, angle=angle)
        img_name = img_name.split('/')[-1].split('.')[0]
        imageSprites[img_name] = image_Sprite
    return imageSprites
